/*
 * Tracker.cc
 *
 */

#include "Tracker.h"
#include <omnetpp.h>
#include <algorithm>

namespace peermaliziosi {

using namespace std;


Tracker::Tracker() {

    numSeeders=0;
    seederStart=0;
}

Tracker::~Tracker() {

}

bool Tracker::mustBeSeeder(int peer){

    bool seeder=false;

    cModule *sysM= simulation.getSystemModule();

    if( numSeeders < sysM->par("numSeeders").longValue()){
        seeder=true;
        numSeeders++;
    }

    return seeder;
}

set<int> * Tracker::trackerRequest(int peer, int remainingPieces, string event, int numWanted){

    set<int> * peerSet = NULL;

    if(event == "completed"){

        // un peer comunica al tracker di essere diventato seeder

        if(activePeers.find(peer) != activePeers.end()){


            if(find(seeders.begin(), seeders.end(),peer) != seeders.end()){

                EV <<"Tracker : so gia' che il peer "<< peer <<" e' seeder (NON DOVREBBE ACCADERE)\n";
            }
            else{
                // un leecher e' diventato seeder
                EV <<"Tracker : il peer "<< peer <<" mi ha comunicato di essere diventato seeder\n";
                seeders.push_back(peer);
                activePeers[peer]=remainingPieces;
                numSeeders++;

            }

        }
        else{

            EV <<"Tracker : il peer "<< peer <<" non appartiene allo swarm non puo' diventare seeder (NON DOVREBBE ACCADERE)\n";

        }

    }

    else if(event == "started"){
        // e' arrivata la richiesta di unirsi allo swarm da parte di un peer

        if(activePeers.find(peer) != activePeers.end()){

            EV <<"Tracker : il peer "<< peer <<" fa gia' parte dello swarm (NON DOVREBBE ACCADERE)\n";
        }
        else{

            EV <<"Tracker : il peer "<< peer <<" si e' unito allo swarm\n";

            if(remainingPieces==0) // questo peer entra nello swarm come seeder
                seeders.push_back(peer);

            activePeers.insert(pair<int, int>(peer,remainingPieces));
            peerSet = getPeerList(peer, numWanted);

        }

    }

    else if(event == "regular"){

        // un peer mi ha richiesto nuovi peer
        peerSet = getPeerList(peer, numWanted);
        activePeers[peer]=remainingPieces;

    }

    else if(event == "stopped"){

        // un peer esce dallo swarm

        if(activePeers.find(peer) == activePeers.end()){

            EV <<"Tracker: il peer "<< peer <<" che vuole uscire non fa parte dello swarm (NON DOVREBBE ACCADERE)\n";
        }
        else{

            vector<int>::iterator it = find(seeders.begin(), seeders.end(),peer);

            if(it != seeders.end()){ // il peer che e' uscito era un seeder
                int dist = distance(seeders.begin(),it);
                if(dist<seederStart){
                    seederStart--;
                }
                seeders.erase(it);
                numSeeders--;
            }

            EV <<"Tracker: il peer "<< peer <<" e' uscito dallo swarm\n";
            activePeers.erase(peer);
        }

    }

    return peerSet;


}

set<int> * Tracker::getPeerList(int peer, int numWanted) {

    if(numWanted<=0){
        return NULL;
    }

    set<int> * peerSet= new set<int>;

    bool seeder = (find(seeders.begin(), seeders.end(),peer) != seeders.end());

    int size= activePeers.size() - 1; // - 1 e' perche' chi ha fatto la richiesta e' gia' tra gli activePeers
    int k= 0;

    if(size==0){
        EV <<"Tracker: Non ho peer da restituire\n";
        return NULL;
    }

    if(numWanted>size){ // nello swarm ci sono meno peer di quelli richiesti

        //restituisco tutti gli activePeers

        EV <<"Tracker: restituisco al peer "<<peer <<" la lista di peer (tutti):\n";

        for(map<int, int>::iterator it=activePeers.begin(); it!=activePeers.end(); ++it){

            if(seeder && ((it->first)!=peer) && (find(seeders.begin(), seeders.end(),it->first) != seeders.end())){ // non restituisco ai seeder altri seeder

                continue;
            }

            if ((it->first)!=peer) {
                peerSet->insert(it->first);
                EV << (it->first) <<"\n";
            }
        }

        return peerSet;

    }

    // se nello swarm ci sono piu' peer di quelli che ho richiesto

    set<int> copyActivePeers;

    for(map<int, int>::iterator it=activePeers.begin(); it!=activePeers.end(); ++it){

        if((it->first) != peer)
            copyActivePeers.insert(it->first);

    }

    if(!seeder){


        int count=0;

        // restituisco al peer un numero minimo di seeder in proporzione al numero di pezzi che gli mancano
        // ad esempio 1 seeder ogni 5 pezzi mancanti (comunque sempre minimo un seeder)

        cModule *sysM= simulation.getSystemModule();

        int numSeederForPeer = activePeers[peer] % (sysM->par("remPiecesNeedSeeder").longValue());

        if(numSeederForPeer == 0)
            numSeederForPeer=1;

        EV <<"Tracker: restituisco al peer "<< peer <<" i seeder:\n";

        while(((unsigned)count < seeders.size()) && (count < numSeederForPeer)){

            int index = ((seederStart++) % seeders.size());
            peerSet->insert(seeders.at(index));
            EV << (seeders.at(index)) <<"\n";
            copyActivePeers.erase(seeders.at(index));
            size--;
            numWanted--;
            count++;
        }

    }



    EV <<"Tracker: restituisco al peer "<<peer <<" i peer scelti casualmente:\n";
    while (k<numWanted && k<size) {

        // seleziono casualmente i peer da restituire
        int rand = intuniform(0,copyActivePeers.size()-1);
        set<int>::iterator it(copyActivePeers.begin());
        advance(it,rand);


        if(!(seeder && (find(seeders.begin(), seeders.end(),*it) != seeders.end()))){ // non restituisco ai seeder altri seeder
            peerSet->insert(*it);
            EV << (*it) <<"\n";
        }

        k++;
        copyActivePeers.erase(*it);

    }

    return peerSet;
}


} /* namespace peermaliziosi */
